package com.gitee.l0km.codegen.base.generator;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.net.URI;
import java.nio.ByteBuffer;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.core.io.InputStreamSource;

import com.gitee.l0km.codegen.annotations.DeriveMethod;
import com.gitee.l0km.codegen.annotations.Remote;
import com.gitee.l0km.codegen.base.CodeGenUtils;
import com.gitee.l0km.codegen.base.Method;
import com.gitee.l0km.codegen.base.MethodException;
import com.gitee.l0km.com4j.base.Assert;
import com.gitee.l0km.com4j.basex.TypeNameUtils;
import com.gitee.l0km.com4j.basex.bean.BeanRelativeUtilits;
import com.gitee.l0km.com4j.basex.reflection.generics.TypeResolution;
import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.common.primitives.Primitives;
import com.google.common.reflect.TypeToken;

import static com.google.common.base.Preconditions.checkNotNull;

public class GeneratorUtils {
	public static String NEW_LINE = System.getProperty("line.separator");
	public final static String toVarName(Class<?> type) {
		return toVarName(TypeNameUtils.getTypeName(type, false));
	}
	public final static String toVarName(String type) {
		Matcher match = Pattern.compile("(^|[^A-Z])([A-Z])([^A-Z])").matcher(type);
		StringBuffer buffer = new StringBuffer();
		int c=0;
		while (match.find()) {
			//match.appendReplacement(buffer, match.group(1)+"_"+((c++==0)?match.group(2).toLowerCase():match.group(2))+match.group(3));
			if(c++==0)
				match.appendReplacement(buffer, match.group(1)+"_"+(match.group(2).toLowerCase())+match.group(3));
		}
		match.appendTail(buffer);
		buffer.append("Instance");
		return buffer.toString();
	}
	
	/**
	 * 递归方式在{@code srcExps}中查找{@code exp}的顶级父类,如果没有则返回{@code null}
	 * @param exp
	 * @param srcExps
	 * @param dstExps
	 */
	@SuppressWarnings("unchecked")
	private static final Class<? extends Throwable> getTopThrowable(Class<? extends Throwable> exp,Collection<Class<? extends Throwable>> srcExps,
			List<Class<? extends Throwable>> dstExps){
		Class<? extends Throwable> superClass = (Class<? extends Throwable>) exp.getSuperclass();
		if(exp==Throwable.class){
			return (srcExps.contains(exp))?exp:null;
		}else	if(dstExps.contains(superClass)){
			return null;
		}else{
			Class<? extends Throwable> s = getTopThrowable((Class<? extends Throwable>) superClass,srcExps,dstExps);
			if (null == s)
				s = superClass;
			return (srcExps.contains(s))?s:null;
		}
	}
	public static final List<Class<? extends Throwable>> sortThrowable(Class<? extends Throwable>[] srcExps) {
		return sortThrowable(new ArrayList<Class<? extends Throwable>>(Arrays.asList(srcExps)));
	}
	public static final List<Class<? extends Throwable>> sortThrowable(Collection<Class<? extends Throwable>> srcExps) {
		Assert.notNull(srcExps, "srcExps");
		List<Class<? extends Throwable>> dstExps = new ArrayList<Class<? extends Throwable>>();
		Class<? extends Throwable> superClass,exp;
		while(!srcExps.isEmpty()){
			if(null==(superClass=getTopThrowable((exp=srcExps.iterator().next()),srcExps,dstExps))){
				srcExps.remove(exp);
				dstExps.add(exp);
			}else{
				srcExps.remove(superClass);
				dstExps.add(superClass);
			}
		}
		Collections.reverse(dstExps);
		return dstExps;
	}

	@SafeVarargs
	public static final List<Class<? extends Throwable>> sortAndFilterThrowable(
			Collection<Class<? extends Throwable>> srcExps, Class<? extends Throwable>... filterClass)
			throws MethodException {
		List<Class<? extends Throwable>> exps = sortThrowable(srcExps);
		Class<? extends Throwable> exp;
		for (Iterator<Class<? extends Throwable>> it = exps.iterator(); it.hasNext();) {
			exp = it.next();
			for (Class<? extends Throwable> filter : filterClass) {
				if (null == filter)
					throw new MethodException("the argument [filterClass] must not have null element");
				if (filter.isAssignableFrom(exp)){
					it.remove();
					break;
				}
			}
		}
		return exps;
	}
	
	public static final Map<Object,Object> createMap(Object[] keys,Object[] values) throws MethodException{
		try{
		 return CodeGenUtils.createMap(keys, values);
		}catch(Exception e){
			throw new MethodException(e); 
		}
	}
	public static final <T>Set<T> toSet(T[] a){
		return CodeGenUtils.toSet(a);
	}
	public static final Set<Object> newSet(){
		return new HashSet<Object>();
	}
	public static final <T>Set<T> toSet(Collection<T> a){
		return CodeGenUtils.toSet(a);
	}
	public static final Class<?> methodToStubClass(String pkg, String methodName) throws ClassNotFoundException {
		Assert.notNull(pkg, "pkg");
		Assert.notEmpty(methodName, "methodName");
		StringBuffer buffer = new StringBuffer();
		if(!pkg.isEmpty())
			buffer.append(pkg).append(".");
		buffer.append(CodeGenUtils.firstUpperCase(methodName));
		return CodeGenUtils.forName(buffer.toString());
	}
	public static final String parameterToSetMethod(String parameterName) {
		Assert.notEmpty(parameterName, "parameterName");
		StringBuffer buffer = new StringBuffer("set");
		buffer.append(CodeGenUtils.firstUpperCase(parameterName));
		return buffer.toString();
	}
	
	public static final void addImportedClasses(Map<String, Class<?>> importedList, String... types)
			throws ClassNotFoundException {
			CodeGenUtils.addImportedTypes(importedList, types);
	}
	public static final void addImportedClasses(Map<String, Class<?>> importedList, Class<?>... types)
			throws ClassNotFoundException {
			CodeGenUtils.addImportedTypes(importedList, types);
	}
	public static final void addImportedClasses(Map<String, Class<?>> importedList, Collection<Type> types)
			throws ClassNotFoundException {
			CodeGenUtils.addImportedTypes(importedList, types);
	}
	public static final boolean isSimpleType(Class<?> type){
		return CodeGenUtils.isBaseDataType(type);
	}
	public static final boolean isString(Type type){
		return String.class.equals(type);
	}
	public static final boolean isArrayOfbyte(Type type){
		return byte[].class.equals(type);
	}
	public static final boolean isByteBuffer(Type type){
		return ByteBuffer.class == type;
	}
	public static final boolean isBinary(Type type){
		return isArrayOfbyte(type) || isByteBuffer(type);
	}
	public static final boolean isURI(Type type){
		return URI.class.equals(type);
	}
	public static final Class<?> getArrayOfByteClass(){
		return byte[].class;
	}
	public static final boolean isJavaUtilClass(Type type){
		return (type instanceof Class<?>) && ((Class<?>)type).getPackage().getName().startsWith("java.util");
	}
    public static boolean isCollection(Type type) {
        return null == type ? false : java.util.Collection.class.isAssignableFrom(TypeToken.of(type).getRawType());
    }
    public static boolean isVector(Type type) {
        return null == type ? false : java.util.Vector.class.isAssignableFrom(TypeToken.of(type).getRawType()) ;
    }
	public static final boolean isRemoteException(Class<? extends Throwable> exception){
		return RemoteException.class.isAssignableFrom(exception);
	}
	public static final boolean isException(Class<?> exception){
		return Exception.class.isAssignableFrom(exception);
	}

	public static final boolean hasMessageConstructor(Class<? extends Throwable> exception) {
		try {
			exception.getConstructor(String.class);
			return true;
		} catch (Exception e) {
		} finally {
		}
		return false;
	}
	public static final Class<?> getElementClass(Class<?> clazz) {
		return CodeGenUtils.getElementClass(clazz);
	}
	public static final int getArrayDimension(Class<?> clazz) {
		return clazz.isArray() ?getArrayDimension(clazz.getComponentType())+1:0;
	}
	public static final boolean isVoid(Class<?> clazz){
		return clazz.getName().equals("void");
	}
	public static final Class<?>toObjectType(Class<?> primitive){
		try{
			return CodeGenUtils.getTypeForPrimitive(primitive);
		}catch(Exception e){
		}finally{
		}
		return primitive;
	}
	public static final Type toPrimitiveType(Type type){
		if(type instanceof Class){
			return Primitives.unwrap((Class<?>) type);
		}
		return type;
	}
	public final static String[] getAllTypeNamesForGenericType(String type) {
		return TypeNameUtils.getAllTypeNamesForGenericType(type);
	}
	public final static Class<?>[] getAllClassForGenericType(String type) {
		return TypeNameUtils.getAllClassForGenericType(type);
	}
	public final static String toClassName(String name){
		return CodeGenUtils.firstUpperCase(name);
	}

	public final static Class<?> getTopDeclaringClass(Class<?> clazz) {
		if (null == clazz||null == clazz.getDeclaringClass())
			return clazz;
		return getTopDeclaringClass(clazz.getDeclaringClass());
	}
	
	@SuppressWarnings("unchecked")
	public static final  List<Object> sortBy(Object c,String fieldName) {
		Assert.notNull(c, "c");
		if(c.getClass().isArray()){
			return Arrays.asList(BeanRelativeUtilits.sortByField(((Object[])c),fieldName));
		}else if(c instanceof Collection){
			return BeanRelativeUtilits.sortByField((Collection<Object>)c,fieldName);
		}
		throw new IllegalArgumentException(String.format("the argument c [%s]must be array or Collection",c.getClass().getName()));
	}
	public static final  List<Object> sortByName(Object c) {
		return sortBy(c,"name");
	}
	private static final Comparator<com.gitee.l0km.codegen.base.Method> METHOD_COMPARATOR = new Comparator<com.gitee.l0km.codegen.base.Method>() {
		@Override
		public int compare(com.gitee.l0km.codegen.base.Method o1, com.gitee.l0km.codegen.base.Method o2) {
			return o1.getSignature().compareTo(o2.getSignature());
		}
	};
	
	/**
	 * 返回 {@link DeriveMethod} 注释指定的方法名后缀,没有则返回空
	 * @param method
	 */
	private static String methodSuffix(com.gitee.l0km.codegen.base.Method method){
		DeriveMethod deriveMethod = method.getAnnotation(DeriveMethod.class);
		if(deriveMethod !=null ){
			String[] suffixs = deriveMethod.methodSuffix();
			return suffixs != null && suffixs.length>0 ? suffixs[0]:"";
		}
		return "";
	}
	private static final Comparator<com.gitee.l0km.codegen.base.Method> PORT_NAME_COMPARATOR = new Comparator<com.gitee.l0km.codegen.base.Method>() {
		@Override
		public int compare(com.gitee.l0km.codegen.base.Method o1, com.gitee.l0km.codegen.base.Method o2) {
			String n1 = o1.getName() + methodSuffix(o1);
			String n2 = o2.getName() + methodSuffix(o2);
			return n1.compareTo(n2);
		}
	};
	/**
	 * 按方法签名排序
	 * @param c
	 * @param comparator 用于排序的比较器实例
	 * @return 排序后的方法列表
	 * @since 2.3.0
	 */
	public static final  List<Method> sortBySignature(Iterable<Method> c,Comparator<Method> comparator) {		
		checkNotNull(c,"c is null");
		checkNotNull(comparator,"comparator is null");
		return Ordering.from(comparator).sortedCopy(c);
	}
	/**
	 * 按方法签名排序
	 * @param c
	 * @param comparator 用于排序的比较器实例
	 * @return 排序后的方法列表
	 * @since 2.3.0
	 */
	public static final  List<Method> sortBySignature(Method[] c,Comparator<Method> comparator) {		
		checkNotNull(c,"c is null");
		return sortBySignature(FluentIterable.from(c), comparator);
	}
	/**
	 * 按方法签名排序
	 * @param c
	 * @param comparator 用于排序的比较器实例
	 * @return 排序后的方法列表
	 */
	@SuppressWarnings("unchecked")
	private static final  List<Method> sortBySignature(Object c,Comparator<Method> comparator) {		
		Assert.notNull(c, "c");
		if(c.getClass().isArray()){
			return sortBySignature((Method[])c);
		}
		else if(c instanceof Iterable){
			return sortBySignature((Iterable<Method>)c);
		} else{
			throw new IllegalArgumentException(String.format("the argument c [%s]must be array or Collection of net.gdface.codegen.base.Method",c.getClass().getName()));
		}
	}
	/**
	 * 按方法签名排序
	 * @param c
	 * @return 排序后的方法列表
	 */
	public static final  List<Method> sortBySignature(Object c) {
		return sortBySignature(c,GeneratorUtils.METHOD_COMPARATOR);
	}
	/**
	 * 按方法签名排序
	 * @param c
	 * @return 排序后的方法列表
	 * @since 2.3.0
	 */
	public static final  List<Method> sortBySignature(Iterable<Method> c) {
		return sortBySignature(c,GeneratorUtils.METHOD_COMPARATOR);
	}
	/**
	 * 按方法签名排序
	 * @param c
	 * @return 排序后的方法列表
	 * @since 2.3.0
	 */
	public static final  List<Method> sortBySignature(Method[] c) {
		return sortBySignature(c,GeneratorUtils.METHOD_COMPARATOR);
	}
	/**
	 * 按服务端口名排序
	 * @param c
	 * @return 排序后的方法列表
	 */
	public static final  List<Method> sortByPortName(Object c) {
		return sortBySignature(c,GeneratorUtils.PORT_NAME_COMPARATOR);
	}
	public static final Class<Remote> getAnnotationRemoteClass(){
		return Remote.class;
	}
	/**
	 * 将输入的多行字符串转义为java字符串
	 * @param input 输入字符串
	 * @param lineDelimit 分隔符,为{@code null}则默认为'\n'
	 * @param tripSymbol 为{@code true},去除所有源码注解格式
	 * @return java字符串
	 */
	public static final String asJavaString(String input,String lineDelimit,boolean tripSymbol){
		input = MoreObjects.firstNonNull(input, "").replace("\"", "\\\"");
		if(tripSymbol){
			input = input.replaceAll("\\{@\\w+ +([^\\}]*)\\}", "$1");
		}
		lineDelimit = MoreObjects.firstNonNull(lineDelimit, "\\n");
		String[] list = input.split("(\\n|\\r\\n|\\r)");
		return "\"" + Joiner.on( lineDelimit + "\""+ NEW_LINE +"+\"").join(list) + "\"";
	}
	/**
	 * 将输入的多行字符串转义为java字符串
	 * @param input 输入字符串
	 * @param lineDelimit 分隔符,为{@code null}则默认为'\n'
	 * @return java字符串
	 */
	public static final String asJavaString(String input,String lineDelimit){
		return asJavaString(input,lineDelimit,false);
	}
	/**
	 * 将输入的多行字符串转义为java字符串，分行符为'\n'
	 * @param input 输入字符串
	 * @param tripSymbol 为{@code true},去除所有源码注解格式
	 * @return java字符串
	 * @see #asJavaString(String, String)
	 */
	public static final String asJavaString(String input,boolean tripSymbol){
		return asJavaString(input,null,tripSymbol);
	}	
	/**
	 * 将输入的多行字符串转义为java字符串，分行符为'\n'
	 * @param input 输入字符串
	 * @return java字符串
	 * @see #asJavaString(String, String)
	 */
	public static final String asJavaString(String input){
		return asJavaString(input,null,false);
	}	
	/**
	 * @param input 输入字符串
	 * @param tripSymbol 为{@code true},去除所有源码注解格式
	 * @return 返回多行字符串的第一行
	 */
	public static final String firstLine(String input,boolean tripSymbol){
		input = MoreObjects.firstNonNull(input, "").replace("\"", "\\\"");
		if(tripSymbol){
			input = input.replaceAll("\\{@\\w+ +([^\\}]*)\\}", "$1").replaceAll("(<br>|<p>)","");			
		}
		String[] list = input.split("(\\n|\\r\\n|\\r)");
		return list.length >0 ? list[0] : "";
	}
	/**
	 * @param input 输入字符串
	 * @return 返回多行字符串的第一行
	 */
	public static final String firstLine(String input){
		return firstLine(input,false);	
	}
	public static final String simpleName(String className) {
		return TypeNameUtils.getSimpleName(className);
	}
	
	/**
	 * 返回参数类型在swagger中对应的类型定义字符串
	 * @param type
	 */
	public static final String swaggerDataType(Type type) {
		Class<?> clazz = TypeToken.of(type).getRawType();
		clazz = Primitives.unwrap((Class<?>)type);
		if(clazz.isPrimitive()){
			return clazz.getName();
		}else if(String.class.equals(clazz)){
			return "string";
		}else if(java.sql.Date.class.isAssignableFrom(clazz)){
			return "date";
		}else if(Date.class.isAssignableFrom(clazz)){
			return "date-time";
		}else if( InputStreamSource.class.isAssignableFrom(clazz)){
			return "__file";
		}else {
			return "object";
		}
	}
	
	private static Field searchField(Class<?> clazz,String fieldName){
		if(clazz == null || fieldName == null){
			return null;
		} 
		for(Field field:clazz.getDeclaredFields()){
			if(!Modifier.isStatic(field.getModifiers()) && field.getName().equals(fieldName)){
				return field;
			}
		}
		return searchField(clazz.getSuperclass(),fieldName);
	}
	private static java.lang.reflect.Method searchMethod(Class<?> clazz,String fieldName){
		if(clazz == null || fieldName == null){
			return null;
		}	
		String n = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
		try {
			return clazz.getMethod("get" +n);
		} catch (NoSuchMethodException e) {
			try {
				return clazz.getMethod("is" +n);
			} catch (NoSuchMethodException e1) {
				
			}
		}
		return null;
	}
	@SuppressWarnings("unchecked")
	public static <T extends Annotation> T extractFieldAnnotation(PropertyDescriptor input,Class<T> clazz) {
		if(input == null || clazz == null){
			return null;
		}
		Annotation[] fieldAnnotation;

		fieldAnnotation = input.getReadMethod().getAnnotations();
		Optional<Annotation> found = Iterables.tryFind(Lists.newArrayList(fieldAnnotation), Predicates.instanceOf(clazz));
		if(found.isPresent()){
			return (T) found.get();
		}else{
			Field field = searchField(input.getReadMethod().getDeclaringClass(),input.getName());
			if(field != null){
				fieldAnnotation = field.getAnnotations();
			}
		}
		return (T) Iterables.tryFind(Lists.newArrayList(fieldAnnotation), Predicates.instanceOf(clazz)).orNull();
	}
	@SuppressWarnings("unchecked")
	public static <T extends Annotation> T extractFieldAnnotation(Field input,Class<T> clazz) {
		if(input == null || clazz == null){
			return null;
		}
		Annotation[] fieldAnnotation = input.getAnnotations();
		Optional<Annotation> found = Iterables.tryFind(Lists.newArrayList(fieldAnnotation), Predicates.instanceOf(clazz));
		if(found.isPresent()){
			return (T) found.get();
		}else{
			java.lang.reflect.Method method = searchMethod(input.getDeclaringClass(),input.getName());
			if(method != null){
				fieldAnnotation = method.getAnnotations();
			}
		}
		return (T) Iterables.tryFind(Lists.newArrayList(fieldAnnotation), Predicates.instanceOf(clazz)).orNull();
	}
	/**
	 * <p>基于目标类型所属的类，解析目标类型为完整类型并返回解析后的类型对象。</p>
	 * @since 3.6.1
	 * @see TypeResolution#resolveType(Class, Type)
	 */
	public static Type resolveType(Class<?> clazz, Type typeOfTarget) {
		return TypeResolution.resolveType(clazz, typeOfTarget);
	}
}
